iT邦幫忙

2024 iThome 鐵人賽

DAY 16
1
Modern Web

Dive into CSS Challenge:從問題到解決方案的實踐之旅系列 第 16

Day 16 - CSS Challenge #9:Rainy Night - Weather UI (下)

  • 分享至 

  • xImage
  •  

題目

CSS Challenge Day9
https://ithelp.ithome.com.tw/upload/images/20240926/20169403Uu6JEZvXsn.png

題目是一個看起來像是 Weather Widget 的介面,配上下雨的動畫,並且在初次 loading 時,月亮會有升起的動畫。

上面的圖是題目,而我們要做出幾乎一樣的樣子,題目中還有附上出題官方的CodePen,也有附上給我們解題用的template,當我們真的不會的時候,還是可以參考他們的寫法,所以沒有想像中困難。

我做好的此題CSS Challeage解答

那麼我們就開始吧。

題目分析

這個題目要求我們製作一個天氣 Widget 的畫面

  • 分為上下兩個區塊
    1. 上方圖片
    2. 下方天氣資訊
  • 圖片分為三個部分
    1. 月亮及動畫(初始時由下方升起)
    2. 夜空與後面的山坡地
    3. 下雨的動畫

前情提要

我們上次已經做完 下方天氣資訊、月亮與動畫、夜空與後面的山坡地,最後完成的畫面長這樣:
https://ithelp.ithome.com.tw/upload/images/20240929/201694036fkAFwp0kz.png

那我們今天先來做上方圖片裡面最後的區域,也就是。
3. 下雨的動畫

開始解題

下雨的動畫

基礎架構

.frame
	.view
		.moon
            ...
        // ───新增的部分
            - for i in (1..10)
			div class="rain-big-#{i}"
			div class="rain-mid-#{i}"
			div class="rain-small-#{i}"
        // ───新增的部分
	.label
    ...

這段代碼使用了迴圈來生成多個雨滴元素,並將它們分為三種尺寸:大、中、小。

每次迴圈會產生一組大小不同的雨滴元素,類別名稱依次為 rain-big-{i}rain-mid-{i}rain-small-{i},其中 {i} 是從 1 到 10 的變量。

這樣做可以讓每一個雨滴元素的樣式和位置保持一致的邏輯規則,同時提供對不同尺寸雨滴的區分。

雨滴樣式

其實雨滴的圓錐型,是由三個大小不同的圓形,用堆雪人的方式組成的。
最下方的是最大的圓形,中間跟最上面分別用兩個偽類 &:before, &:after 組成。
中間的是 &:before,最上面最小的是 &:after
大概知道他的繪製概念之後,我們就開始來製作他的樣式。

$rainBg: #7FC1F9;
$rainWH: 8px;

.view {
    ...
    @for $i from 1 through 10 {
		.rain-big-#{$i} {
			position: absolute;
			width: $rainWH;
			height: $rainWH;
			left: (-20 + 39 * $i ) + px;
			bottom: 0;
			background: $rainBg;
			border-radius: 50%;
			transform-origin: 50% 100%;
			z-index: 20;
		}
    }
}

以尺寸最大的雨滴來做示範,這邊我先做最下面的圓點

這段程式碼使用 @for 迴圈來生成 10 個 .rain-big 雨滴元素。每個雨滴的位置和動畫根據 $i 變數進行調整,具體如下:

  • left: (-20 + 39 * $i ) + px:是用來計算每個雨滴(例如 .rain-big-1, .rain-big-2, ...)在水平方向上的位置。讓我詳細解釋:
    1. $i 是迴圈變數:在 @for $i from 1 through 10 的迴圈中,$i 的值從 1 到 10,代表第幾個雨滴。
    2. 計算過程:
      • 39 * $i
        將 39 與 $i 相乘,這部分決定了每個雨滴之間的水平間距(39 像素)。
      • -20 + (39 * $i):在乘積的基礎上減去 20,這個調整使整個雨滴序列向左移動,確保第一個雨滴不會過於靠右,並將所有雨滴置於適當的位置。
    3. 加上單位:+ px 將計算結果轉換為有效的 CSS 長度值,單位為像素。
      均勻分佈雨滴:透過這個計算,每個雨滴在水平方向上以 39px 的間距排列,從左側的 19px 開始,直到右側的 370px。這使得雨滴在畫面上均勻分佈,模擬自然的雨勢。

減去 20 是為了校正起始位置,確保第一個雨滴不會離左邊緣太遠,調整整個雨滴序列的位置,使其更居中或符合設計需求。

39 是雨滴之間的水平間距。選擇這個數字可以根據設計需要來決定,確保雨滴之間的距離適當,既不過於擁擠,也不過於分散。

  • 形狀與陰影:圓形雨滴透過 border-radius: 50% 呈現圓形的樣子,transform-origin 確保動畫從底部開始。

雨滴的基部做好之後會長這樣:
https://ithelp.ithome.com.tw/upload/images/20240929/2016940393oysZFZJB.png

接著我們準備加上雨滴的頂部跟中間部分

&:before, &:after {
	content: '';
	position: absolute;
	display: block;
	background: $rainBg;
	width: 6px;
	height: 6px;
	border-radius: 50%;
	top: -2px;
	left: 1px;
}

這邊大致上跟基部有一樣的設計,不同的是尺寸,並且位置也稍稍做了調整。
這邊由於 &:before&:after 會需要一樣的樣式,所以我們先設定一樣的。

剛做好的時候會長這樣:
https://ithelp.ithome.com.tw/upload/images/20240929/201694039UKcJgwYAX.png

接著就來製作雨滴的上半部

&:after {
	top: -5px;
	left: 2px;
	width: 4px;
	height: 10px;
}

我們再在 &:before&:after 的底下加上 &:after ,並調整一下不同的寬高及位置。

這樣雨滴的樣式就完成了:
https://ithelp.ithome.com.tw/upload/images/20240929/20169403NOuN9DqVBI.png

雨滴動畫

@keyframes animate-rain {
	0% {
		transform: translate(40px,-320px) scaleX(1) scaleY(1) rotate(17deg);
	}
	85% {
		transform: translate(0,0) scaleX(1) scaleY(1) rotate(17deg);
	}
	100% {
		transform: translate(0,0) scaleX(3) scaleY(0) rotate(0deg);
	}
}

先寫出這段關於控制雨滴動畫的變化效果的動畫,具體說明如下:

  • 0%:動畫開始,雨滴位於 (40px, -320px),也就是從右上角開始,並以 17度 的角度旋轉,比例為原始大小 (scaleX(1), scaleY(1))。

  • 85%:雨滴移動至 (0, 0),維持原始大小和旋轉角度。

  • 100%:雨滴到達終點,比例變成橫向放大三倍 (scaleX(3)),縱向壓扁 (scaleY(0)),並且不再旋轉 (rotate(0deg)),模擬雨滴碰到地面散開的效果。

寫好之後,我們就把這段套用到剛剛的 .rain-big=#{i} 裡面。

.rain-big-#{$i} {
    animation: animate-rain (0.7 + random(2) / 10) + s linear (random(50) / 25) + s infinite;
}

加上動畫的設定

  • animate-rain:指定應用的動畫名稱。

  • (0.7 + random(2) / 10) + s:這是動畫的持續時間。random(2) 會隨機生成 0 或 1,所以動畫持續時間會在 0.7 秒到 0.8 秒之間變化,讓每個雨滴的動畫有輕微差異。

  • linear:定義動畫的運行速度為線性,也就是動畫過程中速度保持一致。

  • (random(50) / 25) + s:這部分定義動畫的延遲時間,random(50) 隨機生成 0 到 49 之間的數字,並除以 25,因此延遲時間會在 0 到 1.96 秒之間變化,確保不同的雨滴不會同時開始動畫。

  • infinite:讓動畫無限循環。

animation-fill-mode: both;

最後在動畫的下方加上這句,控制動畫在「開始前」和「結束後」的狀態。如果設置為 both,這表示動畫會在執行完畢後,保持最後一個關鍵幀(例如 100% 的狀態),而在動畫開始前,元素也會以第一個關鍵幀(例如 0% 的狀態)進行渲染。這樣可以避免動畫結束後立即回復到原始狀態或動畫開始前沒有效果的問題。

如此一來,雨滴的動畫就完成了,再依照這個模式製作 .rain-mid-#{$i}.rain-small-#{$i},這邊我們一樣可以 @extend .rain-big-1 以避免程式碼重複,接著調整它們的透明度跟大小,就可以做出不同尺寸,不同顏色的雨滴了

https://ithelp.ithome.com.tw/upload/images/20240929/20169403aYKskEJaAC.png


Wrap up and go home

希望改變了這種按照步驟的寫法,能讓更多人看得懂,也能跟我一樣喜歡上寫CSS。

那今天就先到這裡,明天我們再繼續來玩下一集。


上一篇
Day 15 - CSS Challenge #9:Rainy Night - Weather UI (中)
下一篇
Day 17 - CSS Challenge #10:Watch UI(上)
系列文
Dive into CSS Challenge:從問題到解決方案的實踐之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言